Crackme 08 by ReptileCrackeR [zip]
  • Type de la protection : nom / numéro de série
  • Méthode utilisée : keygening
  • Outils utilisés :

On perd pas de temps, le programme est compressé avec UPX (voir tutz sur le crackme d'hErmol pour vous rafraîchir la mémoire), on le décompresse vite fait, et on ouvre avec OllyDbg. Clic-droit, 'Search for', 'All referenced text strings', on voit la chaîne "Sorry " en remontant un peu, on double-clique, et nous voilà à un endroit très intéressant. J'espère que pour vous ceci est devenu presque comme un réflexe. On ne voit pas explicitement les appels aux API car c'est codé en Delphi, mais on s'y retrouve quand même, vous verrez. On remonte.

00454B49 |> 837D E8 01 ....CMP DWORD PTR SS:[EBP-18],1
00454B4D |. 75 35 .........JNZ SHORT Crackme8.00454B84
00454B4F |. 68 644C4500 ...PUSH Crackme8.00454C64 .........; ASCII "Thanks "
00454B54 |. 8D55 C8 .......LEA EDX,DWORD PTR SS:[EBP-38]
00454B57 |. 8B45 FC .......MOV EAX,DWORD PTR SS:[EBP-4]
00454B5A |. 8B80 F0020000 .MOV EAX,DWORD PTR DS:[EAX+2F0]
00454B60 |. E8 D7F0FDFF ...CALL Crackme8.00433C3C
00454B65 |. FF75 C8 .......PUSH DWORD PTR SS:[EBP-38]
00454B68 |. 68 744C4500 ...PUSH Crackme8.00454C74
.........; ASCII ", code a keygen now !"
00454B6D |. 8D45 CC .......LEA EAX,DWORD PTR SS:[EBP-34]
00454B70 |. BA 03000000 ...MOV EDX,3
00454B75 |. E8 46FAFAFF ...CALL Crackme8.004045C0
00454B7A |. 8B45 CC .......MOV EAX,DWORD PTR SS:[EBP-34]
00454B7D |. E8 9A8EFDFF ...CALL Crackme8.0042DA1C
00454B82 |. EB 33 .........JMP SHORT Crackme8.00454BB7
00454B84 |> 68 944C4500 ...PUSH Crackme8.00454C94
.........; ASCII "Sorry "
00454B89 |. 8D55 C0 .......LEA EDX,DWORD PTR SS:[EBP-40]
00454B8C |. 8B45 FC .......MOV EAX,DWORD PTR SS:[EBP-4]
00454B8F |. 8B80 F0020000 .MOV EAX,DWORD PTR DS:[EAX+2F0]
00454B95 |. E8 A2F0FDFF ...CALL Crackme8.00433C3C
00454B9A |. FF75 C0 .......PUSH DWORD PTR SS:[EBP-40]
00454B9D |. 68 A44C4500 ...PUSH Crackme8.00454CA4
.........; ASCII ", bad serial !"

On voit l'appel de la bonne MessageBox, de plus en plus intéressant.
Au-dessus quelques tests, on monte, on monte et on voit que la routine commence en 004549E0, posons donc un breakpoint !
On exécute le prog avec F9, on entre nom et numéro, on clique sur le bouton, et ça breake, arf.
Etudions le code maintenant. Je vais pas décrire toutes les lignes, mais les plus importantes pour aller plus vite.

00454A0C |. BA 144C4500 ...MOV EDX,Crackme8.00454C14 ......; ASCII "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345.."
En 00454A0C le programme charge une chaîne "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"...

00454A22 |. E8 15F2FDFF ..CALL Crackme8.00433C3C
00454A27 |. 837D E0 00 ...CMP DWORD PTR SS:[EBP-20],0
00454A2B |. 75 0F ... ....JNZ SHORT Crackme8.00454A3C
00454A2D |. 8D45 F0 ......LEA EAX,DWORD PTR SS:[EBP-10]
00454A30 |. BA 444C4500 ..MOV EDX,Crackme8.00454C44
.......; ASCII "xxxxxxxxxxxxxxxxxxxx"
Ensuite en 454A27 on vérifie si on a rentré un nom, sinon on indique que le nom entré est "xxxxxxxxxxxxxxxxxxxx".

On charge ensuite le nom et le numéro.

00454A61 |. E8 9AFAFAFF
.. ..CALL Crackme8.00404500
00454A66 |. 83F8 14
...... ..CMP EAX,14
00454A69 |. 75 13
......
.. ..JNZ SHORT Crackme8.00454A7E
00454A6B |. 8D55 F8
...... ..LEA EDX,DWORD PTR SS:[EBP-8]
00454A6E |. 8B45 FC
...... ..MOV EAX,DWORD PTR SS:[EBP-4]
00454A71 |. 8B80 F4020000
.. MOV EAX,DWORD PTR DS:[EAX+2F4]
00454A77 |. E8 C0F1FDFF
.. ..CALL Crackme8.00433C3C
00454A7C |. EB 0D
......
. ...JMP SHORT Crackme8.00454A8B
00454A7E |> 8D45 F8
..... ...LEA EAX,DWORD PTR SS:[EBP-8]
00454A81 |. BA 444C4500
.. ..MOV EDX,Crackme8.00454C44 .......s; ASCII "xxxxxxxxxxxxxxxxxxx"

Puis en 00454A66 on regarde si le nombre de caractères pour le numéro est de 20 (0x14 en hexadécimal), si ce n'est pas le cas on remplace le numéro entré par "xxxxxxxxxxxxxxxxxxxx". Donc dans ce crackme on sait déjà qu'il faut rentré un nom (mais ça on s'en doutait déjà), et que le numéro de série doit être de 20 caractères.

00454A9F |. E8 5CFAFAFF
.. ..CALL Crackme8.00404500
00454AA4 |. 83F8 14
.. .. .. CMP EAX,14
00454AA7 |. 7E 1B
.. .. .. ..JLE SHORT Crackme8.00454AC4
00454AA9 |. 8D55 D4
.. .. .. LEA EDX,DWORD PTR SS:[EBP-2C]
00454AAC |. 8B45 FC
.. .. .. MOV EAX,DWORD PTR SS:[EBP-4]
00454AAF |. 8B80 F0020000
.. MOV EAX,DWORD PTR DS:[EAX+2F0]
00454AB5 |. E8 82F1FDFF
.. ..CALL Crackme8.00433C3C
00454ABA |. 8B45 D4
.. .. .. MOV EAX,DWORD PTR SS:[EBP-2C]
00454ABD |. E8 3EFAFAFF
.. ..CALL Crackme8.00404500
00454AC2 |. EB 05
.. .. .. ..JMP SHORT Crackme8.00454AC9
00454AC4 |> B8 14000000
.. ..MOV EAX,14

Le programme compare ensuite le nombre de caractères du nom avec 20 (ou 0x14), si on a plus de 20 caractères, on entre ce nombre dans EAX, sinon, EAX est à 20, ce qui nous servira de compteur pour boucler ensuite. Pour l'instant notre nom fait moins de 20 caractères, nous verrons pourquoi après.

Vient ensuite la routine de génération du numéro.
Mais ici le programme ne fera pas la comparaison entre les 2 chaînes à la fin, il fait la comparaison caractère par caractère, et mettra un boléen (variable qui ne peut être que VRAI ou FAUX) à VRAI si finalement le numéro correspond, voici donc la routine commentée :

00454AC4 |> B8 14000000 .. ..MOV EAX,14 .....................; on initialise la boucle à 20 passages
00454AC9 |> 33DB
.. .... ....XOR EBX,EBX ....................; EBX = 0
00454ACB |. 33F6
.. .... ....XOR ESI,ESI ....................; ESI = 0
00454ACD |. 85C0
.. .... ....TEST EAX,EAX
00454ACF |. 7E 78
.. .... ...JLE SHORT Crackme8.00454B49
00454AD1 |. 8945 E4
.. .... .MOV DWORD PTR SS:[EBP-1C],EAX ..; valeur qui nous servira de compteur
00454AD4 |. C745 EC 01000000 MOV DWORD PTR SS:[EBP-14],1
.....; il s'agit de la valeur qui permettra
00454ADB |> 43
.. .... .... .INC EBX ; EBX = EBX + 1.........; de savoir le nombre de passage effectué
00454ADC |. 46
.. .... .... .INC ESI ; ESI = ESI + 1
00454ADD |. 8B45 F0
.. .... .MOV EAX,DWORD PTR SS:[EBP-10] ..; on charge le nom
00454AE0 |. 33D2
.. .... ....XOR EDX,EDX ; EDX = 0
00454AE2 |. 8A5418 FF
.. ....MOV DL,BYTE PTR DS:[EAX+EBX-1] .; on met dans DL un caractère du nom
00454AE6 |. 8B45 EC
.. .... .MOV EAX,DWORD PTR SS:[EBP-14]
00454AE9 |. 8D0440
.. .... ..LEA EAX,DWORD PTR DS:[EAX+EAX*2] ; EAX = 3 x nombre de passage fait
00454AEC |. 03D0
.. .... ....ADD EDX,EAX ....................; EDX = EDX + EAX
00454AEE |. B8 64000000
.. ..MOV EAX,64 .....................; nous allons boucler 100 fois (0x64)
00454AF3 |> 83FA 24
.. .... .CMP EDX,24
00454AF6 |. 7E 03
.. .... ...JLE SHORT Crackme8.00454AFB ....; si EDX > 36 (0x24)
00454AF8 |. 83EA 24
.. .... .SUB EDX,24 .....................; on soustrait 36 à EDX
00454AFB |> 48
.. .... .... .DEC EAX
00454AFC |.^75 F5
.. .... ...JNZ SHORT Crackme8.00454AF3
00454AFE |. 8B45 F8
.. .... .MOV EAX,DWORD PTR SS:[EBP-8] ...; on charge le numéro entré
00454B01 |. 8A4430 FF
.. ....MOV AL,BYTE PTR DS:[EAX+ESI-1] .; on prend un caractère
00454B05 |. 8B4D F4
.. .... .MOV ECX,DWORD PTR SS:[EBP-C] ...; on charge la chaîne "ABC..."
00454B08 |. 3A4411 FF
.. ....CMP AL,BYTE PTR DS:[ECX+EDX-1] .; on le compare avec l'un de la chaîne
00454B0C |. 75 08
. ..... ...JNZ SHORT Crackme8.00454B16 ....; "ABC..." à la position EDX
00454B0E |. 8B45 E8
. ..... .MOV EAX,DWORD PTR SS:[EBP-18]...; si la lettre = la valeur booléenne
00454B11 |. 8945 E8
. ..... .MOV DWORD PTR SS:[EBP-18],EAX ..; ne change pas
00454B14 |. EB 05
. ..... ...JMP SHORT Crackme8.00454B1B
00454B16 |> 33C0
. ..... ....XOR EAX,EAX ....................; sinon on met le booléen à FAUX
00454B18 |. 8945 E8
. ..... .MOV DWORD PTR SS:[EBP-18],EAX ..; donc numéro entré invalide
00454B1B |> 8D55 D0
. ..... .LEA EDX,DWORD PTR SS:[EBP-30]
00454B1E |. 8B45 FC
. ..... .MOV EAX,DWORD PTR SS:[EBP-4]
00454B21 |. 8B80 F0020000
. .MOV EAX,DWORD PTR DS:[EAX+2F0]
00454B27 |. E8 10F1FDFF
. ...CALL Crackme8.00433C3C
00454B2C |. 8B45 D0
. ..... .MOV EAX,DWORD PTR SS:[EBP-30]
00454B2F |. E8 CCF9FAFF
. ...CALL Crackme8.00404500 .........; on compte le nbre de car. du nom entré
00454B34 |. 3BD8
. ..... ....CMP EBX,EAX ....................; si on est arrivé au dernier car. du nom
00454B36 |. 75 02
. ..... ...JNZ SHORT Crackme8.00454B3A ....; on recommence avec la 1ère lettre du nom
00454B38 |. 33DB
. ..... ....XOR EBX,EBX
00454B3A |> 83FE 14
. ..... .CMP ESI,14
00454B3D |. 75 02
. ..... ...JNZ SHORT Crackme8.00454B41
00454B3F |. 33F6
. ..... ....XOR ESI,ESI
00454B41 |> FF45 EC
. ..... .INC DWORD PTR SS:[EBP-14] ......; on incrémente le nbre de passages fait
00454B44 |. FF4D E4
. ..... .DEC DWORD PTR SS:[EBP-1C] ......; on décrémente le nbre de passag. à faire
00454B47 |.^75 92
. ..... ...JNZ SHORT Crackme8.00454ADB ....; a-t-on fini de vérifier le numéro ?

Avec ces renseignements on peut à présent faire le keygener :
int nbr, nbre_passage_restant, nbre_passage_fait, posN, posS, posC;
char name[20];
char serie[21];
char chaine[40]="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

nbr=GetDlgItemText(hwndDlg, IDC_EDIT1, name, 21);
if (nbr==20)
SetDlgItemText (hwndDlg, IDC_EDIT1, name);

nbre_passage_restant = 20;
nbre_passage_fait = 1;

posN = 0;
posS = 0;

do
{
posN++;
posS++;
posC = 0;
posC = (int) name[posN - 1];
posC += 3*nbre_passage_fait;

while (posC > 36)
posC -= 36;

serie[posS - 1] = chaine[posC - 1];

if (posN==nbr)
posN = 0;

nbre_passage_fait++;
nbre_passage_restant--;
}
while (nbre_passage_restant != 0);

SetDlgItemText (hwndDlg, IDC_EDIT2, serie);

Vous avez sûrement remarqué que je n'ai pas commenté le code entre 00454B3A et 00454B3F, tout simplement parce que c'est un bug du programme, étudions ça quand même, ça ne coûte rien, c'est bonus.

Le principe pour l'instant est que si nous avons entré un nom de moins de 20 caractères, nous bouclerons donc sur le nom afin de générer le numéro, c'est-à-dire qu'arrivé à la fin du nom, on reviendra au début, d'où la ligne 00454B38. Mais si on entre un nom de plus de 20 caractères nous ne bouclerons pas 20 fois, mais autant de fois qu'il y a de lettres dans le nom entré. Or :
00454B3A |> 83FE 14 . .... . .CMP ESI,14
00454B3D |. 75 02
. .... . ...JNZ SHORT Crackme8.00454B41
00454B3F |. 33F6
. .... . .... XOR ESI,ESI
nous indique que si nous en sommes à notre 20ème passage, le caractère à vérifier du numéro entré revient à 0.

Pour la génération du numéro ça pose pas de problème, mais si on entre le numéro ainsi généré, le crackme ne le prendra pas valide, et pourquoi ? Eh bien en bouclant de la sorte sur le numéro, on a écrasé le 1er numéro et/ou plus, et donc lors de la vérification, le numéro n'est plus bon.

Prenons un exemple : Le_MaLaDe/G85QDVJTT7ZWH4MAKKYQ impeccable, notre nom fait moins de 20 caractères. Avec le keygener créé on entre par exemple : Le_MaLaDe - ShmeitCorp et nous obtenons :
Le_MaLaDe - ShmeitCo/G85QDVJTTZF5NBJELZP0
Si nous tenions compte exactement de la vérification, nous aurions sans troncature :
Le_MaLaDe - ShmeitCorp/675QDVJTTZF5NBJELZP0

Nous pouvons voir que les 2 premiers chiffres des numéros de série sont différents, qui correspondent aux 2 caractères supplémentaires.
En effet le fait de boucler sur le numéro de série a remplacé les anciens numéros qui étaient corrects pour les 1ers caractères du nom, par des numéros qui sont corrects pour les derniers caractères, et donc quand le programme voudra vérifier les premiers caractères, eh bien ça ne correspondra plus. Enfin je ne sais pas si j'ai été clair, mais pour moi ça l'est... donc c'est pour cela qu'on ne prendra en compte que 20 caractères pour le nom.